import { getMatchRatioAndCount, getLoopNext, sRand, getValidIndexes } from './math'
import { TEMP, edgeUnitToTempBoxes, pushBoxData } from './box'
import { Matrix4 } from './_imports'
import type { MagizTypes, StyleParsed, Temp } from './_imports'

export { handleVertical }

function handleVertical(
  result: MagizTypes.DataJSON.ModelData,
  parsedStyle: Pick<StyleParsed.FloorResult, 'diverse' | 'vertical' | 'elevations'>,
  rayLoops: Temp.Ray[][]
) {
  const { diverse, vertical, elevations } = parsedStyle
  rayLoops.forEach((rayLoop) => {
    rayLoop.forEach((ray) => {
      if (diverse) {
        // 每层单独生成
        elevations.forEach((elevation) => {
          vertical.forEach((params) => raySpacing(result, [elevation], ray, params))
        })
      } else {
        // 生成后复制到每层
        vertical.forEach((params) => {
          raySpacing(result, elevations, ray, params)
        })
      }
    })
  })
}

/** 沿一段线按间距组合阵列Boxes */
function raySpacing(
  result: MagizTypes.DataJSON.ModelData,
  elevations: number[],
  ray: Temp.Ray,
  params: StyleParsed.EdgeArray
) {
  const { ctrlArray, sandwich, endWidth, array, diverse } = params

  // 计算参数在该段上生成时的批数和缩放系数
  const distance = ray.direction.length()
  const spaces: number[] = []
  array.forEach((edgeUnit) => {
    for (let i = 0; i < edgeUnit.count; i++) spaces.push(edgeUnit.space)
  })
  const rc = getMatchRatioAndCount(spaces, distance, endWidth, sandwich)
  if (rc) {
    /** 可以生成元素的序号列表 */
    const validIndexes = getValidIndexes(rc.count, ctrlArray)
    if (diverse) {
      // 没次生成都重新计算
      elevations.forEach((elevation) => {
        const placeMatrix = initMatrix(ray, endWidth, rc.ratio, elevation)

        /** 垂直方向可能变化，但眼射线方向长度固定，仅计算一次 */
        let arrayRangeScaled = 0
        // 按序号推送数据
        validIndexes.forEach((i) => {
          /** 用于沿ray生成最终的阵列数据 */
          const arrayUnits = initEdgeUnits(array, rc.ratio)
          if (!arrayRangeScaled)
            arrayRangeScaled = arrayUnits.reduce((a, b) => a + b.spaceScaled, 0)
          /** 该序号的起点位置 */
          let startD = i * arrayRangeScaled
          arrayUnits.forEach((tempArrayUnit, n) => {
            pushArraygData(tempArrayUnit, startD, placeMatrix, result)
            startD += (tempArrayUnit.spaceScaled + getLoopNext(arrayUnits, n).spaceScaled) / 2
          })
        })

        // 默认在线段终点不生成元素以按环状阵列，sandwich则会生成array中的第一个box
        if (sandwich) {
          pushArraygData(
            initEdgeUnits(array, rc.ratio)[0]!,
            arrayRangeScaled * rc.count,
            placeMatrix,
            result
          )
        }
      })
    } else {
      // 预先计算好后再复制到每个点位
      /** 用于沿ray生成最终的阵列数据 */
      const arrayUnits = initEdgeUnits(array, rc.ratio)
      /** 缩放后整个组合的长度 */
      const arrayRangeScaled = arrayUnits.reduce((a, b) => a + b.spaceScaled, 0)

      // 按标高推送阵列数据到结果
      // 层生成是根据diverse推送全部elevations或一层的elevation到该函数，validIndexes也会每层不同，因此无须考虑
      elevations.forEach((elevation) => {
        if (arrayUnits.length > 0) {
          const placeMatrix = initMatrix(ray, endWidth, rc.ratio, elevation)
          // 按序号推送数据
          validIndexes.forEach((i) => {
            /** 该序号的起点位置 */
            let startD = i * arrayRangeScaled
            arrayUnits.forEach((tempArrayUnit, n) => {
              pushArraygData(tempArrayUnit, startD, placeMatrix, result)
              startD += (tempArrayUnit.spaceScaled + getLoopNext(arrayUnits, n).spaceScaled) / 2
            })
          })

          // 默认在线段终点不生成元素以按环状阵列，sandwich则会生成array中的第一个box
          if (sandwich)
            pushArraygData(arrayUnits[0]!, arrayRangeScaled * rc.count, placeMatrix, result)
        }
      })
    }
  }
}

/** 初始化矩阵。先将元素在原点处缩放、旋转+移动，再移动到边线上的起点 */
function initMatrix(
  ray: Temp.Ray,
  endWidth: number,
  scaleRatio: number,
  elevation: number
): Matrix4 {
  // 按缩放后的数据偏移ray.start
  const start = ray.start.clone()
  if (endWidth) {
    const d = ray.direction.clone()
    start.add(d.setLength(endWidth * scaleRatio))
  }

  return new Matrix4()
    .makeRotationZ(ray.direction.angle())
    .premultiply(TEMP.makeTranslation(start.x, start.y, elevation))
}

/** 将解析好的参数转为arrayUnits数据，用于沿ray生成最终的阵列数据 */
function initEdgeUnits(edgeArray: StyleParsed.EdgeUnit[], ratio: number): Temp.ArrayUnit[] {
  const result: Temp.ArrayUnit[] = []
  edgeArray.forEach((edgeUnit) => {
    const { space, replace } = edgeUnit
    const tempBoxes = edgeUnitToTempBoxes(edgeUnit, ratio)
    const replaceBoxes = replace ? edgeUnitToTempBoxes(replace, ratio) : undefined
    const spaceScaled = space * ratio

    for (let i = 0; i < edgeUnit.count; i++) {
      result.push({
        spaceScaled,
        tempBoxes: replace && sRand() < replace.chance ? replaceBoxes : tempBoxes,
      })
    }
  })
  return result
}

/** 计算并推送最终的matrix和colorIndex */
function pushArraygData(
  tempArrayUnit: Temp.ArrayUnit,
  targetDistance: number,
  placeMatrix: Matrix4,
  result: MagizTypes.DataJSON.ModelData
) {
  tempArrayUnit.tempBoxes?.forEach((tempBoxData) => {
    const mtx = tempBoxData.matrix
      .clone()
      .premultiply(TEMP.makeTranslation(targetDistance, 0, 0))
      .premultiply(placeMatrix)
    pushBoxData(result, tempBoxData.color, mtx)
  })
}
